/*
 * Puerta.cpp
 *
 */
#include <stdlib.h>
#include <sstream>
#include "Puerta.h"
#include "logger/logger.h"

namespace API{

Puerta::Puerta(doorTypeEnum doorType):
		m_entryRequestQueue(NULL),
		m_entryReplyQueue(NULL),
		m_exitRequestQueue(NULL),
		m_exitReplyQueue(NULL),
		m_semaphore("/tmp/entry_park_sem",'a',1),
		m_capacity("/tmp/entry_park_shm",'a'),
		m_doRun(true),
		m_doorType(doorType)
{

	if(m_doorType == IN_DOOR || m_doorType == CLIENT )
	{
		m_entryRequestQueue =  new MessageQueue2<entryParkRequest>("/tmp/entry_park",'a');
		m_entryReplyQueue  =  new MessageQueue2<entryParkReply>("/tmp/entry_park",'b');
	}

	if(m_doorType == OUT_DOOR || m_doorType == CLIENT )
	{
		m_exitRequestQueue  =  new MessageQueue2<exitParkRequest>("/tmp/exit_park",'a');
		m_exitReplyQueue  =  new MessageQueue2<exitParkReply>("/tmp/exit_park",'b');
	}
}

Puerta::~Puerta()
{
	delete m_entryRequestQueue;
	delete m_entryReplyQueue;
	delete m_exitRequestQueue;
	delete m_exitReplyQueue;

}

void Puerta::init()
{
	m_semaphore.init();

	if(m_entryReplyQueue)
		m_entryReplyQueue->destroy();

	if(m_entryRequestQueue)
		m_entryRequestQueue->destroy();

	if(m_exitReplyQueue)
		m_exitReplyQueue->destroy();

	if(m_exitRequestQueue)
		m_exitRequestQueue->destroy();

	m_capacity.write(2);
}

void Puerta::destroy()
{
	if(m_entryReplyQueue)
		m_entryReplyQueue->destroy();

	if(m_entryRequestQueue)
		m_entryRequestQueue->destroy();

	if(m_exitReplyQueue)
		m_exitReplyQueue->destroy();

	if(m_exitRequestQueue)
		m_exitRequestQueue->destroy();
}

bool Puerta::entrarParque(PuertaId nPuerta,Persona &persona, int &error)
{
	bool retVal;
	entryParkRequest req;
	entryParkReply rep;
	std::stringstream ss;

	if(nPuerta < 0)
		throw Exception("N puerta debe ser mayor a 0");

	ss << "Persona "<< persona.m_id<<" va a entrar al parque por la puerta:"<<nPuerta;

	CLogger::log(ss.str(),LogLevelEnum::INFO);

	req.m_persona.m_id = persona.m_id;

	m_entryRequestQueue->put(nPuerta,req);

	rep = m_entryReplyQueue->get(persona.m_id);

	ss.str("");
	ss << "Persona "<< persona.m_id;

	if(rep.tickets == 1)
	{
		retVal = true;
		ss << " logro entrar al parque.";
	}
	else
	{
		retVal = false;
		ss << " no pudo entrar al parque.";
	}

	CLogger::log(ss.str(),LogLevelEnum::INFO);

	return retVal;
}

void Puerta::salirParque(PuertaId nPuerta,Persona &persona, int &error)
{
	exitParkRequest req;
	exitParkReply rep;
	std::stringstream ss;

	if(nPuerta < 0)
			throw Exception("N puerta debe ser mayor a 0");

	ss << "Persona "<< persona.m_id<<" va a salir del parque.";

	CLogger::log(ss.str(),LogLevelEnum::INFO);

	req.m_persona.m_id = persona.m_id;

	m_exitRequestQueue->put(nPuerta,req);

	rep = m_exitReplyQueue->get(persona.m_id);

	ss.str("");
	ss << "Persona "<< persona.m_id<<" salio del parque";

	CLogger::log(ss.str(),LogLevelEnum::INFO);
}

void Puerta::run(PuertaId nPuerta)
{
	if(nPuerta < 0)
			throw Exception("N puerta debe ser mayor a 0");

	if(m_doorType == IN_DOOR)
	{
		entryParkReply reply;
		entryParkRequest req;

		int capacity;

		while(m_doRun)
		{
			std::stringstream ss;

			req = m_entryRequestQueue->get(nPuerta);

			ss << "Persona:"<<req.m_persona.m_id<<" intentando entrar al parque";
			CLogger::log(ss.str(),LogLevelEnum::INFO);

			m_semaphore.dec();

			capacity = m_capacity.read();

			//TODO ajustar la logica a los tickets
			if(capacity>0)
			{
				m_capacity.write(capacity-1);
				reply.tickets =1;
			}
			else
				reply.tickets =0;

			m_semaphore.inc();

			m_entryReplyQueue->put(req.m_persona.m_id,reply);
		}
	}

	if(m_doorType == OUT_DOOR)
	{
		exitParkReply reply;
		exitParkRequest req;

		int capacity;

		while(m_doRun)
		{
			std::stringstream ss;

			req = m_exitRequestQueue->get(nPuerta);

			ss << "Persona:"<<req.m_persona.m_id<<" intentando salir del parque";
			CLogger::log(ss.str(),LogLevelEnum::INFO);

			m_semaphore.dec();

			capacity = m_capacity.read();

			m_capacity.write(capacity+1);

			m_semaphore.inc();

			m_exitReplyQueue->put(req.m_persona.m_id,reply);
		}
	}
}

}//end namespace API
